#ifndef ALGORITHM_HPP_
#define ALGORITHM_HPP_

class Algorithm;
typedef const Algorithm& AlgorithmRef;

#include "ipopt.hpp"
#include "Problem.hpp"
#include "SubProblem.hpp"
#include "Synchro.hpp"

using namespace Ipopt;

class Algorithm {
public:
	Algorithm(ProblemRef problemDefinition, Index procCount);
	~Algorithm();

	//TODO podzielic to na private i friend dla SubProblem i SynchProblem
	ProblemRef problemDefinition;
	Index *procStartIndex; //length = p+1 (lastEntry == dims)
	Number *optFunDiagMatrix; //length equals dimensions
	Number *lastOptX; //length equals dimensions
	//jakobiany
	Index orgNnzJac;
	Index *jacIRow, *jacJCol; //length orgNnzJac
	Index *procToJacElemNum; // lengtth procCount
	Index *procToJacBlockNum; // length = procCount
	Index *procToJacBlockNumDist; //length = procCount + 1
	Index *restrToProcJacBlockInd; //length = restrictions
	Index subProblemsCount;
	Index dim; //dimension of the problem
	Index restrictions;
	SubProblem** subProblems;
	Number *x_l, *x_u, *g_l, *g_u; //ograniczenia

	static Number toleranceLevel;
	static Index maxSteps;
	static bool _checkInternalDerivatives;
	static unsigned int verboseLevel;
	static bool solveWithIpopt;
	long timeParallel;
	long timeParallelSummed;
	long timeOverall;
	long timeSynchro;

	//dla SubProblme
	Index varToProc(Index varInd) const;
	Index nnzJacIndToProc(Index orgNzi) const;

	void checkDerivatives();
	ApplicationReturnStatus solve();
	unsigned int getSteps() {
		return step;
	}

private:
	IpoptApplication* appFactory();
	void appInit(IpoptApplication *app, bool synchro);
	void doStep();
	void computeOptimalityFunction();
	void computeJacobianData();
	void allocateStructures();
	void deallocateStructures();

	IpoptApplication* apps;
	Synchro* synch;
	Number gradientNorm;
	Number optimalityFun;
	Number* xOrgForOpt;
	Number* restrForOpt;
	Index step;
};

#endif /* ALGORITHM_HPP_ */
